/**
 * 这段代码是Vue的响应式系统的一部分，主要负责管理watcher队列，以及在合适的时机执行这些watcher。
 * watcher用于监听Vue组件或数据的变化，并在变化发生时执行相应的回调函数。
 * 该代码通过维护一个队列来批量处理这些变化，以提高性能。
 * 同时，它还包含了一些用于处理组件激活和更新钩子的逻辑。
 */
// 导入Watcher类型定义，用于类型检查
import type Watcher from './watcher'
import config from '../config'
// 导入Dep类和cleanupDeps函数，用于依赖管理和清理
import Dep, { cleanupDeps } from './dep'
// 导入生命周期钩子函数
import { callHook, activateChildComponent } from '../instance/lifecycle'

import { warn, nextTick, devtools, inBrowser, isIE } from '../util/index'
// 导入Component类型定义，用于类型检查
import type { Component } from 'types/component'

// 定义最大更新次数常量，用于检测无限更新循环
export const MAX_UPDATE_COUNT = 100


const queue: Array<Watcher> = [] // 定义用于存储待执行watcher的队列
const activatedChildren: Array<Component> = [] // 定义用于存储已激活子组件的队列

// 定义一个对象，用于标记已处理的watcher ID
let has: { [key: number]: true | undefined | null } = {}
// 定义一个对象，用于记录循环更新的次数
let circular: { [key: number]: number } = {}

let waiting = false // 定义一个标志，表示是否正在等待执行队列
let flushing = false // 定义一个标志，表示是否正在刷新队列
let index = 0 // 定义一个索引，用于遍历队列
 
/**
 * 重置调度器的状态。
 * Reset the scheduler's state.
 */
function resetSchedulerState() {
  // 重置索引和队列长度
  index = queue.length = activatedChildren.length = 0
  has = {} // 重置has对象
  if (__DEV__) { // 如果是开发模式，则重置circular对象
    circular = {}
  }
  waiting = flushing = false // 重置等待和刷新标志
}

// Async edge case #6566 requires saving the timestamp when event listeners are
// attached. However, calling performance.now() has a perf overhead especially
// if the page has thousands of event listeners. Instead, we take a timestamp
// every time the scheduler flushes and use that for all event listeners
// attached during that flush.

//Async edge case #6566要求在附加事件监听器时保存时间戳。
//但是，调用performance.now（）会带来性能开销，特别是在页面有数千个事件侦听器的情况下。
//相反，我们在调度程序每次刷新时获取一个时间戳，并将其用于在刷新期间附加的所有事件侦听器。

export let currentFlushTimestamp = 0 // 定义一个变量，用于存储当前刷新时间戳

// Async edge case fix requires storing an event listener's attach timestamp.
let getNow: () => number = Date.now // 定义一个函数，用于获取当前时间戳

// Determine what event timestamp the browser is using. Annoyingly, the
// timestamp can either be hi-res (relative to page load) or low-res
// (relative to UNIX epoch), so in order to compare time we have to use the
// same timestamp type when saving the flush timestamp.
// All IE versions use low-res event timestamps, and have problematic clock
// implementations (#9632)

// 根据浏览器环境选择合适的时间戳获取方式
if (inBrowser && !isIE) {
  const performance = window.performance
  if (
    performance &&
    typeof performance.now === 'function' &&
    getNow() > document.createEvent('Event').timeStamp
  ) {
    // if the event timestamp, although evaluated AFTER the Date.now(), is
    // smaller than it, it means the event is using a hi-res timestamp,
    // and we need to use the hi-res version for event listener timestamps as
    // well.
    // 如果浏览器支持高性能时间戳，则使用它
    getNow = () => performance.now()
  }
}
// 定义一个比较函数，用于对watcher队列进行排序
const sortCompareFn = (a: Watcher, b: Watcher): number => {
  // 根据post属性和id进行排序
  if (a.post) {
    if (!b.post) return 1
  } else if (b.post) {
    return -1
  }
  return a.id - b.id
}

/**
 * 刷新队列并执行watchers。
 * Flush both queues and run the watchers.
 */
function flushSchedulerQueue() {
  currentFlushTimestamp = getNow() // 更新当前刷新时间戳
  flushing = true // 设置刷新标志
  let watcher, id

  // Sort queue before flush.
  // This ensures that:
  // 1. Components are updated from parent to child. (because parent is always
  //    created before the child)
  // 2. A component's user watchers are run before its render watcher (because
  //    user watchers are created before the render watcher)
  // 3. If a component is destroyed during a parent component's watcher run,
  //    its watchers can be skipped.
   // 对队列进行排序
  queue.sort(sortCompareFn)

  // do not cache length because more watchers might be pushed
  // as we run existing watchers
  // 遍历队列并执行watchers
  for (index = 0; index < queue.length; index++) {
    watcher = queue[index]
    if (watcher.before) {
      watcher.before()
    }
    id = watcher.id
    has[id] = null
    watcher.run()
    // in dev build, check and stop circular updates.
    // 在开发模式下，检查并停止循环更新
    if (__DEV__ && has[id] != null) {
      circular[id] = (circular[id] || 0) + 1
      if (circular[id] > MAX_UPDATE_COUNT) {
        warn(
          'You may have an infinite update loop ' +
            (watcher.user
              ? `in watcher with expression "${watcher.expression}"`
              : `in a component render function.`),
          watcher.vm
        )
        break
      }
    }
  }

  // keep copies of post queues before resetting state
  // 保存post队列的副本，然后重置状态
  const activatedQueue = activatedChildren.slice()
  const updatedQueue = queue.slice()

  resetSchedulerState()

  // call component updated and activated hooks
   // 调用组件的updated和activated钩子
  callActivatedHooks(activatedQueue)
  callUpdatedHooks(updatedQueue)
  cleanupDeps()

  // devtool hook
  /* istanbul ignore if */
   // 触发devtools钩子
  if (devtools && config.devtools) {
    devtools.emit('flush')
  }
}

function callUpdatedHooks(queue: Watcher[]) {
  let i = queue.length
  while (i--) {
    const watcher = queue[i]
    const vm = watcher.vm
    // 如果vm存在且watcher是组件的渲染watcher且组件已挂载且未销毁，则调用updated钩子
    if (vm && vm._watcher === watcher && vm._isMounted && !vm._isDestroyed) {
      callHook(vm, 'updated')
    }
  }
}

/**
 * Queue a kept-alive component that was activated during patch.
 * The queue will be processed after the entire tree has been patched.
 */
/**
 * 将一个保持活动的组件添加到激活队列中。
 * 该队列将在整个树被修补后处理。
 */
export function queueActivatedComponent(vm: Component) {
  // setting _inactive to false here so that a render function can
  // rely on checking whether it's in an inactive tree (e.g. router-view)
  // 设置_inactive为false，以便渲染函数可以依赖检查它是否在不活动的树中
  vm._inactive = false
  activatedChildren.push(vm)
}

function callActivatedHooks(queue) {
  // 遍历激活队列，调用组件的activate钩子
  for (let i = 0; i < queue.length; i++) {
    queue[i]._inactive = true
    activateChildComponent(queue[i], true /* true */)
  }
}

/**
 * Push a watcher into the watcher queue.
 * Jobs with duplicate IDs will be skipped unless it's
 * pushed when the queue is being flushed.
 */
/**
 * 将一个watcher推入watcher队列。
 * 具有重复ID的作业将被跳过，除非它是在队列正在刷新时推送的。
 */
export function queueWatcher(watcher: Watcher) {
  const id = watcher.id
  // 如果已处理过该watcher，则直接返回
  if (has[id] != null) {
    return
  }
  // 如果watcher是当前Dep的target且设置了noRecurse，则直接返回
  if (watcher === Dep.target && watcher.noRecurse) {
    return
  }
 // 标记该watcher为已处理
  has[id] = true
  // 如果不在刷新状态，则将watcher添加到队列中
  if (!flushing) {
    queue.push(watcher)
  } else {
    // if already flushing, splice the watcher based on its id
    // if already past its id, it will be run next immediately.
    // 如果正在刷新，则根据id将watcher插入到合适的位置
    let i = queue.length - 1
    while (i > index && queue[i].id > watcher.id) {
      i--
    }
    queue.splice(i + 1, 0, watcher)
  }
  // queue the flush
  // 队列化刷新操作
  if (!waiting) {
    waiting = true
  // 如果是开发模式且未启用异步更新，则直接刷新队列
    if (__DEV__ && !config.async) {
      flushSchedulerQueue()
      return
    }
    // 否则，使用nextTick进行异步刷新
    nextTick(flushSchedulerQueue)
  }
}
